# -*- coding: utf-8 -*-
"""
彗星亮度预测工具 v7.3
- 支持彗星和小行星
- 根据选择自动调整输入字段
- 修复输入错误
- 新增数据自动识别功能
"""
import threading
import re
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from tkinter import ttk, messagebox, scrolledtext
import tkinter as tk
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
matplotlib.use('TkAgg')

# ---------- 中文显示 ----------
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False


# ---------- 主程序 ----------
class CometBrightnessApp:
    def __init__(self, root: tk.Tk):
        self.root = root
        self.root.title("彗星亮度预测工具 v7.3")
        self.root.geometry("1400x900")
        self.root.minsize(1200, 700)

        # 数据缓存
        self.current_dates = None
        self.cobs_calc_m_pred = None
        self.cobs_obs_m_pred = None
        self.astro_m_pred = None
        self.mpc_m_pred = None
        self.r_pred = None
        self.delta_pred = None

        # 子窗口引用
        self.brightness_window = None
        self.distance_window = None

        # 进度条变量
        self.progress_var = tk.IntVar(value=0)
        self.progress_text = tk.StringVar(value="就绪")

        self._build_ui()
        self.root.after(100, self.fill_sample_data)

    # -------------------- UI 构建 --------------------
    def _build_ui(self):
        main_paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        main_paned.pack(fill=tk.BOTH, expand=True)

        left_frame = ttk.Frame(main_paned, width=450)
        main_paned.add(left_frame, weight=0)

        right_frame = ttk.Frame(main_paned)
        main_paned.add(right_frame, weight=1)

        # 左侧：参数 + 控制
        self._build_left(left_frame)

        # 右侧：结果 + 进度
        self._build_right(right_frame)

    def _build_left(self, master):
        notebook = ttk.Notebook(master)
        notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        basic = ttk.Frame(notebook)
        notebook.add(basic, text="基本参数")
        self._build_basic_page(basic)

        model = ttk.Frame(notebook)
        notebook.add(model, text="模型参数")
        self._build_model_page(model)

        ctrl = ttk.Frame(notebook)
        notebook.add(ctrl, text="预测控制")
        self._build_control_page(ctrl)

        # 新增：数据导入页面
        import_frame = ttk.Frame(notebook)
        notebook.add(import_frame, text="数据导入")
        self._build_import_page(import_frame)

    def _build_basic_page(self, master):
        # 彗星/小行星选择
        comet_frame = ttk.LabelFrame(master, text="天体选择", padding=10)
        comet_frame.pack(fill=tk.X, pady=5)
        ttk.Label(comet_frame, text="选择天体:").grid(
            row=0, column=0, sticky=tk.W, padx=5)
        self.comet_type_var = tk.StringVar()
        cb = ttk.Combobox(
            comet_frame, textvariable=self.comet_type_var, width=25, state="readonly")
        cb["values"] = ["彗星", "小行星"]
        cb.grid(row=0, column=1, padx=5)
        cb.bind("<<ComboboxSelected>>", self.on_comet_type_selected)

        # 彗星信息
        info_frame = ttk.LabelFrame(master, text="天体信息", padding=10)
        info_frame.pack(fill=tk.X, pady=5)
        ttk.Label(info_frame, text="天体名称:").grid(
            row=0, column=0, sticky=tk.W, padx=5)
        self.name_entry = ttk.Entry(info_frame, width=25)
        self.name_entry.grid(row=0, column=1, padx=5)

        # 轨道元素
        orbit_frame = ttk.LabelFrame(master, text="轨道元素", padding=10)
        orbit_frame.pack(fill=tk.X, pady=5)
        labels = [
            ("q (近日点距离 AU)", "perihelion_distance"),
            ("a (半长轴 AU)", "semi_major_axis"),
            ("e (偏心率)", "eccentricity"),
            ("ω (近日点幅角 °)", "arg_perihelion"),
            ("Ω (升交点赤经 °)", "long_asc_node"),
            ("i (轨道倾角 °)", "inclination"),
            ("P (轨道周期 年)", "orbital_period"),
            ("T (近日点时间)", "perihelion_time"),
            ("历元时间", "epoch_time")
        ]
        self.orbit_entries = {}
        for idx, (txt, key) in enumerate(labels):
            ttk.Label(orbit_frame, text=txt).grid(
                row=idx, column=0, sticky=tk.W, padx=5)
            ent = ttk.Entry(orbit_frame, width=18)
            ent.grid(row=idx, column=1, padx=5)
            self.orbit_entries[key] = ent

    def _build_model_page(self, master):
        switch = ttk.LabelFrame(master, text="模型开关", padding=10)
        switch.pack(fill=tk.X, pady=5)
        self.use_cobs_calc_var = tk.BooleanVar(value=True)
        self.use_cobs_obs_var = tk.BooleanVar(value=True)
        self.use_astro_var = tk.BooleanVar(value=True)
        self.use_mpc_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(switch, text="启用 MPC 模型",
                        variable=self.use_mpc_var).pack(anchor=tk.W)
        ttk.Checkbutton(switch, text="启用 COBS-Calc 模型",
                        variable=self.use_cobs_calc_var).pack(anchor=tk.W)
        ttk.Checkbutton(switch, text="启用 COBS-Observe 模型",
                        variable=self.use_cobs_obs_var).pack(anchor=tk.W)
        ttk.Checkbutton(switch, text="启用 ASTRO 模型",
                        variable=self.use_astro_var).pack(anchor=tk.W)

        mag = ttk.LabelFrame(master, text="星等参数 (H/n)", padding=10)
        mag.pack(fill=tk.X, pady=5)
        self.cobs_calc_H_entry, self.cobs_calc_n_entry = self._add_hn_row(
            mag, 0, "COBS-Calc")
        self.cobs_obs_H_entry, self.cobs_obs_n_entry = self._add_hn_row(
            mag, 1, "COBS-Observe")
        self.astro_H_entry, self.astro_n_entry = self._add_hn_row(
            mag, 2, "ASTRO")
        self.mpc_H_entry, self.mpc_n_entry = self._add_hn_row(mag, 3, "MPC")

    def _add_hn_row(self, master, row, label):
        ttk.Label(master, text=f"{label} H:").grid(
            row=row, column=0, padx=5, sticky=tk.W)
        h = ttk.Entry(master, width=8)
        h.grid(row=row, column=1, padx=5)
        ttk.Label(master, text="n:").grid(row=row, column=2, padx=5)
        n = ttk.Entry(master, width=8)
        n.grid(row=row, column=3, padx=5)
        return h, n

    def _build_control_page(self, master):
        mode = ttk.LabelFrame(master, text="预测模式", padding=10)
        mode.pack(fill=tk.X, pady=5)
        self.pred_mode = tk.StringVar(value="days")
        ttk.Radiobutton(mode, text="按天数预测", variable=self.pred_mode,
                        value="days").pack(anchor=tk.W)
        ttk.Radiobutton(mode, text="按日期范围预测", variable=self.pred_mode,
                        value="dates").pack(anchor=tk.W)
        self.pred_mode.trace_add("write", self.toggle_pred_mode)

        self.days_frame = ttk.Frame(mode)
        self.days_frame.pack(fill=tk.X, pady=5)
        ttk.Label(self.days_frame, text="预测天数:").pack(side=tk.LEFT, padx=5)
        self.pred_days_entry = ttk.Entry(self.days_frame, width=10)
        self.pred_days_entry.insert(0, "365")
        self.pred_days_entry.pack(side=tk.LEFT, padx=5)

        self.dates_frame = ttk.Frame(mode)
        ttk.Label(self.dates_frame, text="开始日期:").pack(side=tk.LEFT, padx=5)
        self.start_date_entry = ttk.Entry(self.dates_frame, width=12)
        self.start_date_entry.pack(side=tk.LEFT, padx=5)
        ttk.Label(self.dates_frame, text="结束日期:").pack(side=tk.LEFT, padx=5)
        self.end_date_entry = ttk.Entry(self.dates_frame, width=12)
        self.end_date_entry.pack(side=tk.LEFT, padx=5)

        btn = ttk.Frame(master)
        btn.pack(fill=tk.X, pady=10)
        ttk.Button(btn, text="开始预测", command=self.run_prediction).pack(
            side=tk.LEFT, padx=5)
        ttk.Button(btn, text="重置参数", command=self.clear_entries).pack(
            side=tk.LEFT, padx=5)

    def _build_import_page(self, master):
        """构建数据导入页面"""
        import_frame = ttk.LabelFrame(master, text="数据自动识别导入", padding=10)
        import_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # 说明文本
        help_text = """支持从COBS或MPC网站复制数据自动识别：
        
彗星数据示例 (COBS格式):
q (perihelion distance)    0.504093
a (semi-major axes)        84.678817
e (eccentricity)           0.994047
ω (argument of perihelion) 308.3520
Ω (Longitude of ascending node) 335.3310
i (inclination)            4.4728
P (Orbital period in years) 779.2551
T (time of perihelion passage) 2025-09-12 20:12:11
Epoch 2025 Nov 21
Absolute magnitude         8.9
Slope parameter            5.7

小行星数据示例 (MPC格式):
perihelion distance (AU)   2.0484138
eccentricity               0.1340344
argument of perihelion (°) 279.87569
ascending node (°)         222.30272
inclination (°)            1.84380
perihelion date            2022-03-23.29816
absolute magnitude         18.5
semimajor axis (AU)        2.3654678
period (years)             3.64
epoch                      2024-10-17.0
"""
        help_label = ttk.Label(import_frame, text=help_text, justify=tk.LEFT)
        help_label.pack(fill=tk.X, pady=5)

        # 数据输入文本框
        ttk.Label(import_frame, text="粘贴数据:").pack(anchor=tk.W, pady=(10, 5))
        self.data_text = scrolledtext.ScrolledText(
            import_frame, height=12, width=50)
        self.data_text.pack(fill=tk.BOTH, expand=True, pady=5)

        # 按钮框架
        btn_frame = ttk.Frame(import_frame)
        btn_frame.pack(fill=tk.X, pady=10)

        ttk.Button(btn_frame, text="识别并导入数据",
                   command=self.auto_import_data).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="清空数据",
                   command=self.clear_import_data).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="导入示例数据",
                   command=self.import_sample_data).pack(side=tk.LEFT, padx=5)

    def _build_right(self, master):
        top = ttk.Frame(master)
        top.pack(fill=tk.BOTH, expand=True)

        self._build_result(top)

        bottom = ttk.Frame(master, height=40)
        bottom.pack(fill=tk.X, side=tk.BOTTOM, padx=5, pady=2)
        self.progress_bar = ttk.Progressbar(
            bottom, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X, side=tk.LEFT,
                               expand=True, padx=(0, 8))
        ttk.Label(bottom, textvariable=self.progress_text,
                  width=20).pack(side=tk.RIGHT)

        chart = ttk.LabelFrame(top, text="图表窗口", padding=10)
        chart.pack(fill=tk.X, pady=5)
        for txt, cmd in [("打开亮度图表", self.open_brightness_window),
                         ("打开距离图表", self.open_distance_window),
                         ("关闭所有图表", self.close_all_windows)]:
            ttk.Button(chart, text=txt, command=cmd).pack(fill=tk.X, pady=3)

    def _build_result(self, master):
        result = ttk.LabelFrame(master, text="预测结果", padding=10)
        result.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        canvas = tk.Canvas(result)
        scroll = ttk.Scrollbar(result, orient="vertical", command=canvas.yview)
        scrollable = ttk.Frame(canvas)
        scrollable.bind("<Configure>", lambda e: canvas.configure(
            scrollregion=canvas.bbox("all")))
        canvas.create_window((0, 0), window=scrollable, anchor="nw")
        canvas.configure(yscrollcommand=scroll.set)
        canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)

        items = [
            "轨道类型", "近日点日期", "预测总天数", "近地点距离",
            "COBS-Calc最亮星等", "COBS-Calc最亮日期",
            "COBS-Observe最亮星等", "COBS-Observe最亮日期",
            "ASTRO最亮星等", "ASTRO最亮日期",
            "MPC最亮星等", "MPC最亮日期",
            "今日日彗距", "今日地彗距",
            "今日COBS-Calc星等", "今日COBS-Observe星等", "今日ASTRO星等", "今日MPC星等",
            "30天后日彗距", "30天后地彗距", "30天后COBS-Calc星等", "30天后COBS-Observe星等", "30天后ASTRO星等", "30天后MPC星等",
            "60天后日彗距", "60天后地彗距", "60天后COBS-Calc星等", "60天后COBS-Observe星等", "60天后ASTRO星等", "60天后MPC星等",
            "彗发长度 (km)", "彗发长度 (°)", "彗尾长度 (km)", "彗尾长度 (°)"
        ]
        self.info_labels = {}
        for k in items:
            f = ttk.Frame(scrollable)
            f.pack(fill=tk.X, pady=1)
            ttk.Label(f, text=k+":", width=26,
                      anchor="e").pack(side=tk.LEFT, padx=5)
            lab = ttk.Label(f, text="待计算", width=41, anchor="w")
            lab.pack(side=tk.LEFT, padx=5)
            self.info_labels[k] = lab

    # -------------------- 数据导入功能 --------------------
    def auto_import_data(self):
        """自动识别并导入数据"""
        data = self.data_text.get("1.0", tk.END).strip()
        if not data:
            messagebox.showwarning("警告", "请输入要识别的数据")
            return

        try:
            # 先检测小行星数据（更严格的检测）
            if self._detect_and_import_asteroid_data(data):
                messagebox.showinfo("成功", "小行星数据导入成功！")
            # 再检测彗星数据
            elif self._detect_and_import_comet_data(data):
                messagebox.showinfo("成功", "彗星数据导入成功！")
            else:
                messagebox.showerror("错误", "无法识别数据格式，请检查数据格式")
        except Exception as e:
            messagebox.showerror("导入错误", f"数据导入失败: {str(e)}")

    def _detect_and_import_comet_data(self, data):
        """检测并导入彗星数据"""
        lines = data.split('\n')

        # 检测彗星特征关键词
        comet_keywords = ['perihelion distance', 'semi-major axes', 'argument of perihelion',
                          'Longitude of ascending node', 'Absolute magnitude', 'Slope parameter',
                          'Orbital period in years', 'Epoch']
        if not any(keyword in data for keyword in comet_keywords):
            return False

        extracted_data = {}

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # 解析名称
            if 'C/' in line or 'P/' in line or 'SWAN' in line or 'COMET' in line.upper():
                name_match = re.search(
                    r'(C/\d+ [A-Z]+\w*|P/\d+ [A-Z]+\w*|[A-Z]+\d+ [A-Z]+\w*|Comet [A-Z0-9/ ]+)', line, re.IGNORECASE)
                if name_match:
                    extracted_data['name'] = name_match.group(1)

            # 解析轨道参数
            if 'perihelion distance' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['q'] = match.group(1)
            elif 'semi-major axes' in line.lower() or 'semi-major axis' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['a'] = match.group(1)
            elif 'eccentricity' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['e'] = match.group(1)
            elif 'argument of perihelion' in line and '°' not in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['ω'] = match.group(1)
            elif 'longitude of ascending node' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['Ω'] = match.group(1)
            elif 'inclination' in line.lower() and '°' not in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['i'] = match.group(1)
            elif 'orbital period' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['P'] = match.group(1)
            elif 'time of perihelion passage' in line.lower():
                # 匹配日期时间格式
                date_match = re.search(r'(\d{4}-\d{2}-\d{2}[\s\d:]+)', line)
                if date_match:
                    extracted_data['T'] = date_match.group(1).strip()
            elif 'epoch' in line.lower() and not line.lower().startswith('epoch jd'):
                # 匹配历元时间
                epoch_match = re.search(
                    r'(\d{4}[-\s]\w+[-\s]\d+|\d{4}-\d{2}-\d{2})', line)
                if epoch_match:
                    extracted_data['epoch'] = epoch_match.group(1)
            elif 'absolute magnitude' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['H'] = match.group(1)
            elif 'slope parameter' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['n'] = match.group(1)

        # 填充数据到表单
        if extracted_data:
            self.comet_type_var.set("彗星")
            self.clear_entries()

            if 'name' in extracted_data:
                self.name_entry.delete(0, tk.END)
                self.name_entry.insert(0, extracted_data['name'])

            # 填充轨道参数
            orbit_mapping = {
                'q': 'perihelion_distance',
                'a': 'semi_major_axis',
                'e': 'eccentricity',
                'ω': 'arg_perihelion',
                'Ω': 'long_asc_node',
                'i': 'inclination',
                'P': 'orbital_period',
                'T': 'perihelion_time',
                'epoch': 'epoch_time'
            }

            for key, entry_key in orbit_mapping.items():
                if key in extracted_data:
                    self.orbit_entries[entry_key].delete(0, tk.END)
                    self.orbit_entries[entry_key].insert(
                        0, extracted_data[key])

            # 填充星等参数（彗星使用COBS-Calc）
            if 'H' in extracted_data and 'n' in extracted_data:
                self.cobs_calc_H_entry.delete(0, tk.END)
                self.cobs_calc_H_entry.insert(0, extracted_data['H'])
                self.cobs_calc_n_entry.delete(0, tk.END)
                self.cobs_calc_n_entry.insert(0, extracted_data['n'])

                # 启用COBS-Calc模型
                self.use_cobs_calc_var.set(True)

            return True
        return False

    def _detect_and_import_asteroid_data(self, data):
        """检测并导入小行星数据"""
        lines = data.split('\n')

        # 检测小行星特征关键词 - 更严格的检测
        asteroid_keywords = ['perihelion distance', 'eccentricity', 'argument of perihelion',
                             'ascending node', 'inclination', 'absolute magnitude',
                             'semimajor axis', 'period', 'epoch']

        # 检查是否包含小行星特征且不包含彗星特征
        has_asteroid_features = any(keyword in data.lower()
                                    for keyword in asteroid_keywords)
        has_comet_features = any(keyword in data.lower() for keyword in [
                                 'perihelion passage', 'slope parameter', 'c/', 'p/'])

        if not has_asteroid_features or has_comet_features:
            return False

        extracted_data = {}
        asteroid_detected = False

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # 解析名称（小行星编号）
            if '(' in line and ')' in line and any(char.isdigit() for char in line):
                name_match = re.search(r'\((\d+)\)', line)
                if name_match:
                    extracted_data['name'] = f"小行星 {name_match.group(1)}"
                    asteroid_detected = True

            # 解析轨道参数
            if 'perihelion distance' in line.lower() and 'au' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['q'] = match.group(1)
                    asteroid_detected = True
            elif 'eccentricity' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['e'] = match.group(1)
                    asteroid_detected = True
            elif 'argument of perihelion' in line.lower() and '°' in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['ω'] = match.group(1)
                    asteroid_detected = True
            elif 'ascending node' in line.lower() and '°' in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['Ω'] = match.group(1)
                    asteroid_detected = True
            elif 'inclination' in line.lower() and '°' in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['i'] = match.group(1)
                    asteroid_detected = True
            elif 'perihelion date' in line.lower() or 'perihelion date' in line.lower():
                date_match = re.search(r'(\d{4}-\d{2}-\d{2}\.\d+)', line)
                if date_match:
                    extracted_data['T'] = date_match.group(1)
                    asteroid_detected = True
            elif 'semimajor axis' in line.lower() and 'au' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['a'] = match.group(1)
                    asteroid_detected = True
            elif 'period' in line.lower() and 'years' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['P'] = match.group(1)
                    asteroid_detected = True
            elif 'epoch' in line.lower() and not line.lower().startswith('epoch jd'):
                epoch_match = re.search(r'(\d{4}-\d{2}-\d{2}\.\d+)', line)
                if epoch_match:
                    extracted_data['epoch'] = epoch_match.group(1)
                    asteroid_detected = True
            elif 'absolute magnitude' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['H'] = match.group(1)
                    asteroid_detected = True

        # 填充数据到表单
        if extracted_data and asteroid_detected:
            self.comet_type_var.set("小行星")
            self.clear_entries()

            if 'name' in extracted_data:
                self.name_entry.delete(0, tk.END)
                self.name_entry.insert(0, extracted_data['name'])
            else:
                # 如果没有明确名称，使用默认名称
                self.name_entry.delete(0, tk.END)
                self.name_entry.insert(0, "小行星")

            # 填充轨道参数
            orbit_mapping = {
                'q': 'perihelion_distance',
                'a': 'semi_major_axis',
                'e': 'eccentricity',
                'ω': 'arg_perihelion',
                'Ω': 'long_asc_node',
                'i': 'inclination',
                'P': 'orbital_period',
                'T': 'perihelion_time',
                'epoch': 'epoch_time'
            }

            for key, entry_key in orbit_mapping.items():
                if key in extracted_data:
                    self.orbit_entries[entry_key].delete(0, tk.END)
                    self.orbit_entries[entry_key].insert(
                        0, extracted_data[key])

            # 填充星等参数（小行星使用MPC）
            if 'H' in extracted_data:
                self.mpc_H_entry.delete(0, tk.END)
                self.mpc_H_entry.insert(0, extracted_data['H'])
                self.mpc_n_entry.delete(0, tk.END)
                self.mpc_n_entry.insert(0, "2.0")  # 小行星默认n值

                # 启用MPC模型，禁用其他模型
                self.use_mpc_var.set(True)
                self.use_cobs_calc_var.set(False)
                self.use_cobs_obs_var.set(False)
                self.use_astro_var.set(False)

            return True
        return False

    def clear_import_data(self):
        """清空导入数据文本框"""
        self.data_text.delete("1.0", tk.END)

    def import_sample_data(self):
        """导入示例数据"""
        sample_data = """Comet C/2025 R2 (SWAN)
q (perihelion distance)    0.504093
a (semi-major axes)        84.678817
e (eccentricity)           0.994047
ω (argument of perihelion) 308.3520
Ω (Longitude of ascending node) 335.3310
i (inclination)            4.4728
P (Orbital period in years) 779.2551
T (time of perihelion passage) 2025-09-12 20:12:11
Epoch 2025 Nov 21
Absolute magnitude         8.9
Slope parameter            5.7"""

        self.data_text.delete("1.0", tk.END)
        self.data_text.insert("1.0", sample_data)

    # -------------------- 事件 --------------------
    def toggle_pred_mode(self, *_):
        if self.pred_mode.get() == "days":
            self.days_frame.pack(fill=tk.X, pady=5)
            self.dates_frame.pack_forget()
        else:
            self.days_frame.pack_forget()
            self.dates_frame.pack(fill=tk.X, pady=5)
            if not self.start_date_entry.get():
                self.start_date_entry.insert(
                    0, datetime.now().strftime('%Y-%m-%d'))
            if not self.end_date_entry.get():
                self.end_date_entry.insert(
                    0, (datetime.now() + timedelta(days=1825)).strftime('%Y-%m-%d'))

    def on_comet_type_selected(self, *_):
        self.clear_entries()
        if self.comet_type_var.get() == "彗星":
            self._build_comet_input()
        else:
            self._build_asteroid_input()

    def _build_comet_input(self):
        for key in list(self.orbit_entries.keys())[:-1]:  # Remove 'epoch_time'
            self.orbit_entries[key].delete(0, tk.END)
            self.orbit_entries[key].insert(0, "")

        self.orbit_entries["perihelion_time"].delete(0, tk.END)
        self.orbit_entries["perihelion_time"].insert(0, "2025-11-08 12:54:17")

    def _build_asteroid_input(self):
        for key in list(self.orbit_entries.keys()):
            self.orbit_entries[key].delete(0, tk.END)
            self.orbit_entries[key].insert(0, "")

        self.orbit_entries["perihelion_time"].delete(0, tk.END)
        self.orbit_entries["perihelion_time"].insert(0, "2024-08-04.18975")

    # -------------------- 数据 --------------------
    def fill_sample_data(self):
        self.comet_type_var.set("彗星")
        self.fill_comet_data("323P-B/SOHO")

    def fill_comet_data(self, name):
        data = COMET_SAMPLE_DATA.get(name)
        if not data:
            return
        self.clear_entries()
        self.name_entry.insert(0, data["name"])
        for k, v in data["orbit"].items():
            if v:
                self.orbit_entries[k].delete(0, tk.END)
                self.orbit_entries[k].insert(0, str(v))
        self.use_cobs_calc_var.set(data["model_params"]["use_cobs_calc"])
        self.use_cobs_obs_var.set(data["model_params"]["use_cobs_obs"])
        self.use_astro_var.set(data["model_params"]["use_astro"])
        self.use_mpc_var.set(data["model_params"]["use_mpc"])
        self.cobs_calc_H_entry.insert(
            0, str(data["model_params"]["cobs_calc_H"]))
        self.cobs_calc_n_entry.insert(
            0, str(data["model_params"]["cobs_calc_n"]))
        self.cobs_obs_H_entry.insert(
            0, str(data["model_params"]["cobs_obs_H"]))
        self.cobs_obs_n_entry.insert(
            0, str(data["model_params"]["cobs_obs_n"]))
        self.astro_H_entry.insert(0, str(data["model_params"]["astro_H"]))
        self.astro_n_entry.insert(0, str(data["model_params"]["astro_n"]))
        self.mpc_H_entry.insert(0, str(data["model_params"]["mpc_H"]))
        self.mpc_n_entry.insert(0, str(data["model_params"]["mpc_n"]))

    def clear_entries(self):
        for e in self.orbit_entries.values():
           e.delete(0, tk.END)
        for h, n in [(self.cobs_calc_H_entry, self.cobs_calc_n_entry),
                     (self.cobs_obs_H_entry, self.cobs_obs_n_entry),
                     (self.astro_H_entry, self.astro_n_entry),
                     (self.mpc_H_entry, self.mpc_n_entry)]:
            h.delete(0, tk.END)
            n.delete(0, tk.END)
        for lab in self.info_labels.values():
           lab.config(text="待计算")

    # -------------------- 校验 --------------------
    def validate_inputs(self):
        try:
            name = self.name_entry.get().strip()
            if not name:
                raise ValueError("请输入天体名称")
            q = float(self.orbit_entries["perihelion_distance"].get())
            e = float(self.orbit_entries["eccentricity"].get())
            peri_str = self.orbit_entries["perihelion_time"].get()
            a_str = self.orbit_entries["semi_major_axis"].get()
            a = float(a_str) if a_str else self.calc_semi_major_axis(q, e)

            # 修复日期解析错误
            try:
                if self.comet_type_var.get() == "彗星":
                    peri_date = datetime.strptime(
                        peri_str, "%Y-%m-%d %H:%M:%S")
                else:
                    # 小行星日期格式处理
                    if '.' in peri_str:
                        peri_date = datetime.strptime(
                            peri_str.split('.')[0], "%Y-%m-%d")
                    else:
                        peri_date = datetime.strptime(peri_str, "%Y-%m-%d")
            except ValueError:
                raise ValueError(
                    "近日点时间格式错误，彗星请使用 'YYYY-MM-DD HH:MM:SS'，小行星请使用 'YYYY-MM-DD'")

            # 修复星等参数获取错误
            calc_H = self.cobs_calc_H_entry.get()
            calc_n = self.cobs_calc_n_entry.get()
            obs_H = self.cobs_obs_H_entry.get()
            obs_n = self.cobs_obs_n_entry.get()
            astro_H = self.astro_H_entry.get()
            astro_n = self.astro_n_entry.get()
            mpc_H = self.mpc_H_entry.get()
            mpc_n = self.mpc_n_entry.get()

            if not 0.001 <= q <= 1000:
                raise ValueError("近日点距离应在0.001-1000 AU范围内")
            if e < 0:
                raise ValueError("偏心率不能为负")
            if self.use_cobs_calc_var.get() and calc_H and calc_n:
                if not (-50 <= float(calc_H) <= 5000 and -50 <= float(calc_n) <= 5000):
                    raise ValueError("COBS-Calc模型参数应在-50到5000范围内")
            if self.use_cobs_obs_var.get() and obs_H and obs_n:
                if not (-50 <= float(obs_H) <= 5000 and -50 <= float(obs_n) <= 5000):
                    raise ValueError("COBS-Observe模型参数应在-50到5000范围内")
            if self.use_astro_var.get() and astro_H and astro_n:
                if not (-50 <= float(astro_H) <= 5000 and -50 <= float(astro_n) <= 5000):
                    raise ValueError("ASTRO模型参数应在-50到5000范围内")
            if self.use_mpc_var.get() and mpc_H and mpc_n:
                if not (-50 <= float(mpc_H) <= 5000 and -50 <= float(mpc_n) <= 5000):
                    raise ValueError("MPC模型参数应在-50到5000范围内")
            return (name, q, a, e, peri_date, calc_H, calc_n, obs_H, obs_n, astro_H, astro_n, mpc_H, mpc_n)
        except Exception as e:
            messagebox.showerror("输入错误", str(e))
            return None

    def calc_semi_major_axis(self, q, e):
        if e < 1:
            return q / (1 - e)
        elif e == 1:
            return 1e10  # 使用极大值代表无穷大
        else:
            return q / (1 - e)

    # -------------------- 预测 --------------------
    def run_prediction(self):
        inputs = self.validate_inputs()
        if inputs is None:
            return
        threading.Thread(target=self._do_prediction,
                         args=(inputs,), daemon=True).start()

    def _do_prediction(self, inputs):
        self._set_progress(0, "开始计算……")
        try:
            (name, q, a, e, peri_date, calc_H, calc_n, obs_H,
             obs_n, astro_H, astro_n, mpc_H, mpc_n) = inputs
            if self.pred_mode.get() == "days":
                future_days = int(self.pred_days_entry.get())
                if not 1 <= future_days <= 1_000_000:
                    raise ValueError("预测天数应在1-1,000,000范围内")
                start_date = peri_date - timedelta(days=future_days // 2)
                all_dates = [start_date +
                             timedelta(days=i) for i in range(future_days)]
            else:
                start_date = datetime.strptime(
                    self.start_date_entry.get(), '%Y-%m-%d')
                end_date = datetime.strptime(
                    self.end_date_entry.get(), '%Y-%m-%d')
                delta_days = (end_date - start_date).days
                if delta_days > 1_000_000:
                    raise ValueError("预测范围不能超过1,000,000天")
                all_dates = [start_date +
                             timedelta(days=i) for i in range(delta_days + 1)]
            total = len(all_dates)
            self._set_progress(5, "计算日彗距/地彗距……")

            r_pred = []
            for i, date in enumerate(all_dates):
                days_from_peri = (date - peri_date).days
                if e < 1:
                    M = 2 * np.pi * days_from_peri / (a ** 1.5 * 365.25)
                    E = self.solve_kepler(M, e)
                    r = a * (1 - e * np.cos(E))
                elif e == 1:
                    r = self.calc_parabolic(days_from_peri, q)
                else:
                    r = self.calc_hyperbolic(days_from_peri, q, e, a)
                r_pred.append(max(r, 0.001))
                if i % 100 == 0:
                    self._set_progress(5 + 25 * i // total, "计算日彗距/地彗距……")
            r_pred = np.array(r_pred)

            delta_pred = []
            for i, date in enumerate(all_dates):
                delta = self.calc_earth_distance(date, r_pred[i], e, peri_date)
                delta_pred.append(delta)
                if i % 100 == 0:
                    self._set_progress(30 + 20 * i // total, "计算地彗距……")
            delta_pred = np.array(delta_pred)

            self._set_progress(55, "计算星等……")
            self.cobs_calc_m_pred = None
            self.cobs_obs_m_pred = None
            self.astro_m_pred = None
            self.mpc_m_pred = None
            if self.use_cobs_calc_var.get() and calc_H and calc_n:
                self.cobs_calc_m_pred = (float(
                    calc_H) + 5 * np.log10(delta_pred) + 2.5 * float(calc_n) * np.log10(r_pred))
                self.cobs_calc_m_pred = np.clip(
                    self.cobs_calc_m_pred, -100, 5000)
            if self.use_cobs_obs_var.get() and obs_H and obs_n:
                self.cobs_obs_m_pred = float(
                    obs_H) + 5 * np.log10(delta_pred) + 2.5 * float(obs_n) * np.log10(r_pred)
                self.cobs_obs_m_pred = np.clip(
                    self.cobs_obs_m_pred, -100, 5000)
            if self.use_astro_var.get() and astro_H and astro_n:
                self.astro_m_pred = float(
                    astro_H) + 5 * np.log10(delta_pred) + 2.5 * float(astro_n) * np.log10(r_pred)
                self.astro_m_pred = np.clip(self.astro_m_pred, -100, 5000)
            if self.use_mpc_var.get() and mpc_H and mpc_n:
                self.mpc_m_pred = float(
                    mpc_H) + 5 * np.log10(delta_pred) + 2.5 * float(mpc_n) * np.log10(r_pred)
                self.mpc_m_pred = np.clip(self.mpc_m_pred, -100, 5000)

            self.current_dates = all_dates
            self.r_pred = r_pred
            self.delta_pred = delta_pred
            self._set_progress(90, "更新界面……")
            self.update_results(name, e, all_dates, r_pred, total)
            self.update_brightness_window()
            self.update_distance_window()
            self._set_progress(100, "完成")
            messagebox.showinfo("预测完成", "彗星亮度预测计算完成！ (v7.3)")
        except Exception as e:
            self._set_progress(0, "错误")
            messagebox.showerror("预测错误", str(e))

    def _set_progress(self, val, text):
        self.progress_var.set(val)
        self.progress_text.set(text)
        self.root.update_idletasks()

    # -------------------- 轨道算法 --------------------
    def solve_kepler(self, M, e, tol=1e-8, max_iter=100):
        if abs(M) < 1e-6:
            return M
        E = M if e < 0.8 else (np.pi if M > 0 else -np.pi)
        for _ in range(max_iter):
            delta_E = (E - e * np.sin(E) - M) / (1 - e * np.cos(E))
            E -= delta_E
            if abs(E) > 10 * np.pi:
                E = np.sign(E) * 10 * np.pi
            if abs(delta_E) < tol:
                break
        return E

    def calc_parabolic(self, days_from_peri, q):
        W = 3 * days_from_peri * np.sqrt(0.5 / (q ** 3)) / 2
        s = W
        for _ in range(10):
            s_new = W - s ** 3 / 3
            if abs(s_new - s) < 1e-8:
                break
            s = s_new
        return max(q * (1 + s ** 2), 0.001)

    def calc_hyperbolic(self, days_from_peri, q, e, a):
        N = 2 * np.pi * days_from_peri / ((-a) ** 1.5 * 365.25)
        H = self.solve_hyperbolic_kepler(N, e)
        return max(a * (1 - e * np.cosh(H)), 0.001)

    def solve_hyperbolic_kepler(self, N, e, tol=1e-8, max_iter=100):
        if abs(N) < 0.1:
            H = N
        else:
            H = np.sign(N) * np.log(2 * abs(N) / e + 1.8)
        for _ in range(max_iter):
            sinhH, coshH = np.sinh(H), np.cosh(H)
            f = e * sinhH - H - N
            df = e * coshH - 1
            if abs(df) < 1e-12:
                H += 0.1
                continue
            delta = f / df
            H -= delta
            if abs(H) > 100:
                H = np.sign(H) * 100
            if abs(delta) < tol:
                break
        return H

    def calc_earth_distance(self, date, r, e, peri_date):
        days_from_peri = (date - peri_date).days
        earth_angle = 2 * np.pi * (days_from_peri % 365.25) / 365.25
        earth_x, earth_y = np.cos(earth_angle), np.sin(earth_angle)
        omega = np.radians(float(self.orbit_entries["arg_perihelion"].get()))
        i = np.radians(float(self.orbit_entries["inclination"].get()))
        comet_x = r * np.cos(omega)
        comet_y = r * np.sin(omega) * np.cos(i)
        delta = np.sqrt((comet_x - earth_x) ** 2 + (comet_y - earth_y) ** 2)
        return max(delta, 0.001)

    # -------------------- 结果更新 --------------------
    def update_results(self, name, e, dates, r_pred, total):
        orbit_type = f"椭圆轨道 (e={e:.6f})" if e < 1 else "抛物线轨道 (e=1.000000)" if e == 1 else f"双曲线轨道 (e={e:.6f})"
        self.info_labels["轨道类型"].config(text=orbit_type)
        self.info_labels["预测总天数"].config(text=f"{total:,} 天")

        peri_idx = np.argmin(r_pred)
        peri_date_str = dates[peri_idx].strftime('%Y-%m-%d')
        self.info_labels["近日点日期"].config(text=peri_date_str)
        self.info_labels["近地点距离"].config(text=f"{r_pred[peri_idx]:.7f} AU")

        today = datetime.now().date()
        today_idx = next((i for i, date in enumerate(
            dates) if date.date() == today), None)
        if today_idx is not None:
            today_data = {
                "calc": self.cobs_calc_m_pred[today_idx] if self.cobs_calc_m_pred is not None else "N/A",
                "obs": self.cobs_obs_m_pred[today_idx] if self.cobs_obs_m_pred is not None else "N/A",
                "astro": self.astro_m_pred[today_idx] if self.astro_m_pred is not None else "N/A",
                "mpc": self.mpc_m_pred[today_idx] if self.mpc_m_pred is not None else "N/A",
                "r": r_pred[today_idx],
                "delta": self.delta_pred[today_idx]
            }
            self.info_labels["今日日彗距"].config(text=f"{today_data['r']:.7f}")
            self.info_labels["今日地彗距"].config(
                text=f"{today_data['delta']:.7f} AU")
            for k, lbl in zip(["calc", "obs", "astro", "mpc"], ["今日COBS-Calc星等", "今日COBS-Observe星等", "今日ASTRO星等", "今日MPC星等"]):
                if today_data[k] != "N/A":
                    self.info_labels[lbl].config(text=f"{today_data[k]:.2f}")
        for days in [30, 60]:
            future_date = datetime.now() + timedelta(days=days)
            future_idx = next((i for i, date in enumerate(
                dates) if date.date() == future_date.date()), None)
            if future_idx is not None:
                future_data = {
                    "calc": self.cobs_calc_m_pred[future_idx] if self.cobs_calc_m_pred is not None else "N/A",
                    "obs": self.cobs_obs_m_pred[future_idx] if self.cobs_obs_m_pred is not None else "N/A",
                    "astro": self.astro_m_pred[future_idx] if self.astro_m_pred is not None else "N/A",
                    "mpc": self.mpc_m_pred[future_idx] if self.mpc_m_pred is not None else "N/A",
                    "r": r_pred[future_idx],
                    "delta": self.delta_pred[future_idx]
                }
                self.info_labels[f"{days}天后日彗距"].config(
                    text=f"{future_data['r']:.7f}")
                self.info_labels[f"{days}天后地彗距"].config(
                    text=f"{future_data['delta']:.7f} AU")
                for k, lbl in zip(["calc", "obs", "astro", "mpc"], [f"{days}天后COBS-Calc星等", f"{days}天后COBS-Observe星等", f"{days}天后ASTRO星等", f"{days}天后MPC星等"]):
                    if future_data[k] != "N/A":
                        self.info_labels[lbl].config(
                            text=f"{future_data[k]:.2f}")
        if today_idx is not None:
            coma_length_km, coma_length_deg, tail_length_km, tail_length_deg = self.calc_coma_tail(
                r_pred[today_idx], self.delta_pred[today_idx])
            self.info_labels["彗发长度 (km)"].config(text=f"{coma_length_km:.0f}")
            self.info_labels["彗发长度 (°)"].config(text=f"{coma_length_deg:.4f}")
            self.info_labels["彗尾长度 (km)"].config(text=f"{tail_length_km:.0f}")
            self.info_labels["彗尾长度 (°)"].config(text=f"{tail_length_deg:.4f}")

    def calc_coma_tail(self, r, delta):
        coma_length_km = 100000 / np.sqrt(r)
        tail_length_km = 1000000 / np.sqrt(r)
        delta_km = delta * 149597870.7
        coma_length_deg = np.degrees(coma_length_km / delta_km)
        tail_length_deg = np.degrees(tail_length_km / delta_km)
        return coma_length_km, coma_length_deg, tail_length_km, tail_length_deg

    # -------------------- 图表窗口 --------------------
    def open_brightness_window(self):
        if self.brightness_window is None or not self.brightness_window.winfo_exists():
            self.brightness_window = tk.Toplevel(self.root)
            self.brightness_window.title("亮度预测图表 v7.3")
            self.setup_brightness_window()
            if self.current_dates is not None:
                self.update_brightness_window()
        else:
            self.brightness_window.lift()

    def open_distance_window(self):
        if self.distance_window is None or not self.distance_window.winfo_exists():
            self.distance_window = tk.Toplevel(self.root)
            self.distance_window.title("距离变化图表 v7.3")
            self.setup_distance_window()
            if self.current_dates is not None:
                self.update_distance_window()
        else:
            self.distance_window.lift()

    def close_all_windows(self):
        for w in [self.brightness_window, self.distance_window]:
            if w and w.winfo_exists():
                w.destroy()

    def setup_brightness_window(self):
        if not self.brightness_window.winfo_exists():
            return
        self.brightness_fig, self.brightness_ax = plt.subplots(figsize=(12, 8))
        self.brightness_canvas = FigureCanvasTkAgg(
            self.brightness_fig, master=self.brightness_window)
        self.brightness_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        NavigationToolbar2Tk(self.brightness_canvas,
                             self.brightness_window).update()

    def setup_distance_window(self):
        if not self.distance_window.winfo_exists():
            return
        self.distance_fig, self.distance_ax = plt.subplots(figsize=(12, 8))
        self.distance_canvas = FigureCanvasTkAgg(
            self.distance_fig, master=self.distance_window)
        self.distance_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        NavigationToolbar2Tk(self.distance_canvas,
                             self.distance_window).update()

    def update_brightness_window(self):
        if (self.brightness_window is None or not self.brightness_window.winfo_exists() or self.current_dates is None):
            return
        self.brightness_ax.clear()
        e = float(self.orbit_entries["eccentricity"].get())
        orbit_type = "椭圆轨道" if e < 1 else ("抛物线轨道" if e == 1 else "双曲线轨道")
        colors = ['#d62728', '#2ca02c', '#000080', '#ff7f0e']
        labels = ['COBS-Calc', 'COBS-Observe', 'ASTRO', 'MPC']
        predictions = [self.cobs_calc_m_pred,
                       self.cobs_obs_m_pred, self.astro_m_pred, self.mpc_m_pred]
        use_models = [self.use_cobs_calc_var.get(), self.use_cobs_obs_var.get(),
                      self.use_astro_var.get(), self.use_mpc_var.get()]
        for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
            if pred is not None and use:
                self.brightness_ax.plot(
                    self.current_dates, pred, color=color, label=label, linewidth=2)
                min_mag = min(pred)
                min_idx = np.argmin(pred)
                self.brightness_ax.plot(self.current_dates[min_idx], min_mag, 'o',
                                        color=color, markersize=8,
                                        label=f'{label}最亮: {min_mag:.2f}等')
        today = datetime.now().date()
        today_idx = next((i for i, date in enumerate(
            self.current_dates) if date.date() == today), None)
        if today_idx is not None:
            for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
                if pred is not None and use:
                    today_mag = pred[today_idx]
                    self.brightness_ax.plot(self.current_dates[today_idx], today_mag, 's',
                                            color=color, markersize=6,
                                            label=f'今日{label}: {today_mag:.2f}等')
        thirty_days_later = datetime.now() + timedelta(days=30)
        thirty_idx = next((i for i, date in enumerate(
            self.current_dates) if date.date() == thirty_days_later.date()), None)
        if thirty_idx is not None:
            for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
                if pred is not None and use:
                    thirty_mag = pred[thirty_idx]
                    self.brightness_ax.plot(self.current_dates[thirty_idx], thirty_mag, 'D',
                                            color=color, markersize=6,
                                            label=f'30天后{label}: {thirty_mag:.2f}等')
        self.brightness_ax.set_ylabel("视星等")
        self.brightness_ax.set_title(
            f"{self.name_entry.get()}亮度预测 - {orbit_type} (v7.3)", fontsize=14)
        self.brightness_ax.grid(True, alpha=0.3)
        self.brightness_ax.legend()
        self.brightness_ax.invert_yaxis()
        self.brightness_fig.autofmt_xdate()
        self.brightness_fig.tight_layout()
        self.brightness_canvas.draw()

    def update_distance_window(self):
        if (self.distance_window is None or not self.distance_window.winfo_exists() or self.current_dates is None):
            return
        self.distance_ax.clear()
        e = float(self.orbit_entries["eccentricity"].get())
        orbit_type = "椭圆轨道" if e < 1 else ("抛物线轨道" if e == 1 else "双曲线轨道")
        self.distance_ax.plot(self.current_dates, self.r_pred,
                              'orange', label='日彗距', linewidth=2)
        self.distance_ax.plot(
            self.current_dates, self.delta_pred, 'blue', label='地彗距', linewidth=2)

        today_idx = next((i for i, date in enumerate(
            self.current_dates) if date.date() == datetime.now().date()), None)
        if today_idx is not None:
            today_r, today_delta = self.r_pred[today_idx], self.delta_pred[today_idx]
            self.distance_ax.plot(self.current_dates[today_idx], today_r, 's', color='purple', markersize=6,
                                  label=f'今日日彗距: {today_r:.7f} AU')
            self.distance_ax.plot(self.current_dates[today_idx], today_delta, 's', color='green', markersize=6,
                                  label=f'今日地彗距: {today_delta:.7f} AU')
        peri_date_str = self.orbit_entries["perihelion_time"].get()
        if peri_date_str:
            try:
                if self.comet_type_var.get() == "彗星":
                    peri_date = datetime.strptime(
                        peri_date_str, "%Y-%m-%d %H:%M:%S")
                else:
                    peri_date = datetime.strptime(
                        peri_date_str.split('.')[0], "%Y-%m-%d")
                peri_idx = next((i for i, date in enumerate(
                    self.current_dates) if date.date() == peri_date.date()), None)
                if peri_idx is not None:
                    peri_r, peri_delta = self.r_pred[peri_idx], self.delta_pred[peri_idx]
                    self.distance_ax.plot(self.current_dates[peri_idx], peri_r, 'D', color='orange', markersize=6,
                                          label=f'近日点: {peri_r:.7f} AU')
                    self.distance_ax.plot(self.current_dates[peri_idx], peri_delta, 'D', color='cyan', markersize=6,
                                          label=f'近日点地彗距: {peri_delta:.7f} AU')
            except ValueError:
                pass
        self.distance_ax.set_ylabel("距离 (AU)")
        self.distance_ax.set_xlabel("日期")
        self.distance_ax.set_title(
            f"{self.name_entry.get()}距离变化 - {orbit_type} (v7.3)", fontsize=14)
        self.distance_ax.grid(True, alpha=0.3)
        self.distance_ax.legend()

        self.distance_fig.autofmt_xdate()
        self.distance_fig.tight_layout()
        self.distance_canvas.draw()


# -------------------- 样本数据 --------------------
COMET_SAMPLE_DATA = {
    "323P-B/SOHO": {
        "name": "323P-B/SOHO",
        "orbit": {
            "perihelion_distance": "0.040063",
            "semi_major_axis": "2.890131",
            "eccentricity": "0.986138",
            "arg_perihelion": "353.9691",
            "long_asc_node": "323.4650",
            "inclination": "5.4627",
            "orbital_period": "4.9135",
            "perihelion_time": "2025-12-16 05:37:06",
            "epoch_time": "2025-10-06"
        },
        "model_params": {
            "use_cobs_calc": True,
            "use_cobs_obs": True,
            "use_astro": True,
            "use_mpc": True,
            "cobs_calc_H": "20.0",
            "cobs_calc_n": "10.0",
            "cobs_obs_H": "19.5",
            "cobs_obs_n": "9.5",
            "astro_H": "18.5",
            "astro_n": "9.0",
            "mpc_H": "21.0",
            "mpc_n": "5.0"
        }
    },
    # 其他彗星数据...
    "2024-08-04.18975": {
        "name": "小行星示例",
        "orbit": {
            "perihelion_distance": "1.9399403",
            "semi_major_axis": "2.3698099",
            "eccentricity": "0.1813941",
            "arg_perihelion": "125.12058",
            "long_asc_node": "267.64434",
            "inclination": "2.93273",
            "orbital_period": "3.65",
            "perihelion_time": "2024-08-04.18975",
            "epoch_time": "2025-05-05.0"
        },
        "model_params": {
            "use_cobs_calc": False,
            "use_cobs_obs": False,
            "use_astro": False,
            "use_mpc": True,
            "cobs_calc_H": "",
            "cobs_calc_n": "",
            "cobs_obs_H": "",
            "cobs_obs_n": "",
            "astro_H": "",
            "astro_n": "",
            "mpc_H": "15.0",
            "mpc_n": "2.0"
        }
    }
}


# -------------------- 启动 --------------------
if __name__ == "__main__":
    root = tk.Tk()
    app = CometBrightnessApp(root)
    root.mainloop()
